package net.ge.mosq.web.others;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import net.ge.mosq.jdo.entity.AccountItem;
import net.ge.mosq.jdo.entity.AccountType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.ValidatorResources;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class EnvironmentInitialization implements javax.servlet.ServletContextListener {

	private Log log = LogFactory.getLog(EnvironmentInitialization.class);

	/**
	 * Delimitter for Validator resources.
	 */
	private final static String RESOURCE_DELIM = ",";

	/**
	 * Application scope key that <code>ValidatorResources</code> is stored under.
	 */
	public final static String VALIDATOR_KEY = "org.apache.commons.validator.VALIDATOR_RESOURCES";

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet. ServletContextEvent)
	 */
	public void contextDestroyed(ServletContextEvent arg0) {
		//
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet .ServletContextEvent)
	 */
	public void contextInitialized(ServletContextEvent arg0) {
		// load Template AccountItems
		ServletContext sc = arg0.getServletContext();

		Map<String, AccountType> accountTypes = new HashMap<String, AccountType>();
		try {
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();

			String accountTypeFilePath = sc.getRealPath("WEB-INF") + File.separator + "account-types.xml";
			Document document = db.parse(accountTypeFilePath);
			NodeList nodeList = document.getElementsByTagName("type");
			for (int i = 0; i < nodeList.getLength(); i++) {
				NamedNodeMap nnm = nodeList.item(i).getAttributes();
				for (int j = 0; j < nnm.getLength(); j++) {
					accountTypes.put(nnm.getNamedItem("name").getNodeValue(), new AccountType(nnm.getNamedItem("name").getNodeValue(), nnm.getNamedItem("label1").getNodeValue(),
							nnm.getNamedItem("label2").getNodeValue(), nnm.getNamedItem("parent").getNodeValue()));
				}
			}
		} catch (ParserConfigurationException e) {
			log.error(e.getMessage(), e);
		} catch (SAXException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}

		Map<String, String> dispatchPatterns = new HashMap<String, String>();
		try {
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			Document document = db.parse(sc.getRealPath("WEB-INF") + File.separator + "dispatch-config.xml");
			NodeList nodeList = document.getElementsByTagName("url-pattern");
			for (int i = 0; i < nodeList.getLength(); i++) {
				NamedNodeMap nnm = nodeList.item(i).getAttributes();
				nnm.item(1).getNodeValue();
				for (int j = 0; j < nnm.getLength(); j++) {
					dispatchPatterns.put(nnm.getNamedItem("src").getNodeValue(), nnm.getNamedItem("dispatch").getNodeValue());
				}
			}
		} catch (ParserConfigurationException e) {
			log.error(e.getMessage(), e);
		} catch (SAXException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}

		List<AccountItem> accountList = new ArrayList<AccountItem>(), tempList = new ArrayList<AccountItem>();

		try {
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			String templateItemsPath = sc.getRealPath("WEB-INF") + File.separator + "accounts-template.xml";
			Document document = db.parse(templateItemsPath);
			NodeList nodeList = document.getElementsByTagName("Account");
			AccountItem ai = null;
			for (int i = 0; i < nodeList.getLength(); i++) {
				NamedNodeMap nnm = nodeList.item(i).getAttributes();
				ai = new AccountItem();
				tempList.add(ai);
				for (int j = 0; j < nnm.getLength(); j++) {
					Method[] methods = AccountItem.class.getDeclaredMethods();
					for (Method method : methods) {
						if (("set" + nnm.item(j).getNodeName()).equalsIgnoreCase(method.getName())) {
							method.invoke(ai, nnm.item(j).getNodeValue());
						}
					}
				}
			}
			setParent(tempList, null);
			for (AccountItem accountItem : tempList) {
				if (accountItem.getParentName().equals("top")) {
					accountList.add(accountItem);
				}
			}
		} catch (IllegalAccessException e) {
			log.error(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			log.error(e.getMessage(), e);
		} catch (ParserConfigurationException e) {
			log.error(e.getMessage(), e);
		} catch (SAXException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (IllegalArgumentException e) {
			log.error(e.getMessage(), e);
		} catch (DOMException e) {
			log.error(e.getMessage(), e);
		} finally {
		}

		sc.setAttribute("AccountTypes", accountTypes);
		sc.setAttribute("DispatchPatterns", dispatchPatterns);
		sc.setAttribute("TemplateAccounts", accountList);

		// ----validator
		String vpn = sc.getInitParameter("validator-path-names");
		try {
			sc.setAttribute(VALIDATOR_KEY, this.initResources(vpn, sc));
		} catch (IOException e) {
			log.error("Cannot load a validator resource from '" + vpn + "' " + e.getMessage(), e);
		}
	}

	private void setParent(List<AccountItem> accountList, AccountItem parent) {
		if (parent == null) {
			for (AccountItem accountItem : accountList) {
				setParent(accountList, accountItem);
			}
		} else {
			for (AccountItem accountItem : accountList) {
				if (accountItem.getParentName().equals(parent.getName())) {
					if (parent.getChildenList() != null) {
						parent.getChildenList().add(accountItem);
					} else {
						parent.setChildenList(new ArrayList<AccountItem>());
						parent.getChildenList().add(accountItem);
					}
				}
			}
		}
	}

	/**
	 * Initialize the validator resources for this module.
	 * 
	 * @throws IOException
	 *             if an input/output error is encountered
	 */
	protected ValidatorResources initResources(String pathnames, ServletContext servletContext) throws IOException {

		if (pathnames == null || pathnames.length() <= 0) {
			return null;
		}
		StringTokenizer st = new StringTokenizer(pathnames, RESOURCE_DELIM);

		List<InputStream> streamList = new ArrayList<InputStream>();
		try {
			while (st.hasMoreTokens()) {
				String validatorRules = st.nextToken().trim();
				if (log.isInfoEnabled()) {
					log.info("Loading validation rules file from '" + validatorRules + "'");
				}

				InputStream input = servletContext.getResourceAsStream(validatorRules);

				// If the config isn't in the servlet context, try the class loader
				// which allows the config files to be stored in a jar
				if (input == null) {
					input = getClass().getResourceAsStream(validatorRules);
				}

				if (input != null) {
					BufferedInputStream bis = new BufferedInputStream(input);
					streamList.add(bis);
				} else {
					log.error("Skipping validation rules file from '" + validatorRules + "'.  No stream could be opened.");
					return null;
				}
			}
			int streamSize = streamList.size();
			InputStream[] streamArray = new InputStream[streamSize];
			for (int streamIndex = 0; streamIndex < streamSize; streamIndex++) {
				InputStream is = (InputStream) streamList.get(streamIndex);
				streamArray[streamIndex] = is;
			}

			return new ValidatorResources(streamArray);
		} catch (SAXException sex) {
			log.error("Skipping all validation", sex);
		} finally {
			Iterator<InputStream> streamIterator = streamList.iterator();
			while (streamIterator.hasNext()) {
				InputStream is = (InputStream) streamIterator.next();
				is.close();
			}
		}
		return null;
	}
}
